﻿$import( "SmitearWebGL.Renderer.Renderer" );
$import( "SmitearWebGL.Objects.Canvas.Hdr.Luminance" );
$import( "SmitearWebGL.Objects.Canvas.Hdr.DownSample" );
$import( "SmitearWebGL.Objects.Canvas.Hdr.Adaptation" );
$import( "SmitearWebGL.Objects.Canvas.Hdr.Bloom" );
$import( "SmitearWebGL.Objects.Canvas.Hdr.Blur" );
$import( "SmitearWebGL.Objects.Canvas.Hdr.Combine" );

SmitearWebGL.Renderer.HDRRenderer = SmitearWebGL.Renderer.Renderer.extend(
{
	/******** constructor ********/

	init : function()
	{
		this._super();
		this._registClass( "SmitearWebGL.Renderer.HDRRenderer" );
	}
	,
	getFrameBufferObject : function()
	{
		return this._luminanceFBO;
	}
	,
	setSourceTexture : function( sourceTex )
	{
		this._luminanceCanvas.replaceTextureAt( sourceTex, 0 );
		this._bloomCanvas.replaceTextureAt( sourceTex, 0 );
		this._combineCanvas.replaceTextureAt( sourceTex, 0 );
		
		this._luminanceCanvas.calculateOffsets();
		this._bloomCanvas.calculateOffsets();
	}
	,
	render : function( scene )
	{
		if ( ! SmitearWebGL.Scene._hdrEnable ) return SmitearWebGL.Core.HResult.SUCCESS;
		if ( ! scene ) return SmitearWebGL.Core.HResult.UNKNOWN_SCENE;
		
		SmitearWebGL.Objects.Viewport.setClearColor( this.context.gl, SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
		this.context.gl.disable( this.context.gl.CULL_FACE );
		
		//////////////////////////////////计算亮度//////////////////////////////////
		this._luminanceFBO.bind();
		
		//获得亮度图
		this._luminanceFBO.useRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTLumUsage.FBO_LUMINACE_TEX );
		this._luminanceFBO.clearRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
		
		this._luminanceCanvas.renderCallBack();
		
		this._luminanceFBO.unbind();
		//////////////////////////////////结束//////////////////////////////////
		
		//////////////////////////////////计算平均亮度//////////////////////////////////
		//下采样到4*4,得到平均亮度
		this._downsampleCanvas.beforeRender();
		for ( var i = 0; i < this._downSampleFBOs.length; ++ i )
		{
			this._downSampleFBOs[i].bind();
			this._downSampleFBOs[i].useRenderTarget( 0 );
			this._downSampleFBOs[i].clearRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
			this._downsampleCanvas.renderCallBack( i );
			this._downSampleFBOs[i].unbind();
		}
		//下采样到1*1,得到插值平均亮度
		this._adaptationFBO.bind();
		this._adaptationFBO.useRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX2 );
		this._adaptationFBO.clearRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
		this._adaptationCanvas.renderCallBack( scene.getRenderSession() );
		this._adaptationFBO.unbind();
		this._adaptationFBO.swapRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX1, SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX2 );
		//////////////////////////////////结束//////////////////////////////////
		
		//////////////////////////////////提取高亮区域并模糊//////////////////////////////////
		this._bloomFBO.bind();
		
		//提取高亮度部分并下采样到1/4大小
		this._bloomFBO.useRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_BLOOM_TEX );
		this._bloomFBO.clearRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
		
		this._bloomCanvas.renderCallBack();
		
		//横向模糊
		this._bloomFBO.useRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_HBLUR_TEX );
		this._bloomFBO.clearRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
		
		this._hBlurCanvas.renderCallBack();
		
		//纵向模糊
		this._bloomFBO.useRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_VBLUR_TEX );
		this._bloomFBO.clearRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTTClearColor );
		
		this._vBlurCanvas.renderCallBack();
		
		this._bloomFBO.unbind();
		//////////////////////////////////结束//////////////////////////////////
		
		//debug stage
		if ( SmitearWebGL.Renderer.HDRRenderer._Debug )
			this.finalStage();
		else
		{
			SmitearWebGL.Objects.Viewport.clear( this.context.gl );
			this._combineCanvas.renderCallBack();
		}
	}
	,
	finalStage : function()
	{
		SmitearWebGL.Objects.Viewport.setClearColor( this.context.gl, SmitearWebGL.Renderer.HDRRenderer.RTFClearColor );
		SmitearWebGL.Objects.Viewport.clear( this.context.gl );
		
		this._ppCanvas.renderCallBack();
	}
	,
	
	/******** private method ********/
	
	_initFBO : function()
	{
		var sampler0 = this.context.resourceManager.getResourceByName( SmitearWebGL.Resources.InternalSamplers.Type.sampler0 );
		var sampler2 = this.context.resourceManager.getResourceByName( SmitearWebGL.Resources.InternalSamplers.Type.sampler2 );
		var informat = SmitearWebGL.Resources.Texture.Format.ARGB8;
		var format = SmitearWebGL.Resources.Texture.Format.ARGB8;
		var ubyte = SmitearWebGL.Resources.Texture.Type.UBYTE;
		var float32 = SmitearWebGL.Resources.Texture.Type.UBYTE;
		if ( this.context.capacity.supported.OES_texture_float )
			float32 = SmitearWebGL.Resources.Texture.Type.FLOAT32;
		
		//亮度计算
		this._luminanceFBO = this.context.resourceManager.createResource( "__luminance_fbo__", SmitearWebGL.Resources.FrameBufferObject );
		this._luminanceFBO.bind();
		this._luminanceFBO.createRenderSurface( SmitearWebGL.Renderer.HDRRenderer.LumImageSize, SmitearWebGL.Renderer.HDRRenderer.LumImageSize );
		this._luminanceFBO.createRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTLumUsage.FBO_LUMINACE_TEX, informat, format, float32, sampler0 );
		this._luminanceFBO.addRef();
		this._luminanceFBO.unbind();
		
		//平均亮度
		var texSize = 64.0;
		var numTex = parseInt( Math.log( SmitearWebGL.Renderer.HDRRenderer.LumImageSize )
					/ Math.log( SmitearWebGL.Renderer.HDRRenderer.LumDownScale ) + 0.5 ) - 1;
		this._downSampleFBOs = new Array( numTex );
		
		for ( var i = 0; i < numTex; ++ i )
		{
			this._downSampleFBOs[i] = this.context.resourceManager.createResource( "__down_sample_fbo_" + i + "__", SmitearWebGL.Resources.FrameBufferObject  );
			this._downSampleFBOs[i].bind();
			this._downSampleFBOs[i].createRenderSurface( texSize, texSize );
			this._downSampleFBOs[i].createRenderTarget( 0, informat, format, float32, sampler0 );
			this._downSampleFBOs[i].addRef();
			this._downSampleFBOs[i].unbind();
			
			texSize /= SmitearWebGL.Renderer.HDRRenderer.LumDownScale;
		}
		
		//平均亮度插值
		this._adaptationFBO = this.context.resourceManager.createResource( "__adaptation_fbo__", SmitearWebGL.Resources.FrameBufferObject  );
		this._adaptationFBO.bind();
		this._adaptationFBO.createRenderSurface( 1, 1 );
		this._adaptationFBO.createRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX1,
												informat, format, float32, sampler0 );
		this._adaptationFBO.createRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX2,
												informat, format, float32, sampler0 );
		this._adaptationFBO.addRef();
		this._adaptationFBO.unbind();
		
		//提取高亮区域
		var fboWidth = SmitearWebGL.Core.Math.ceilingPowerOfTwo( this.context.viewWidth );
		var fboHeight = SmitearWebGL.Core.Math.ceilingPowerOfTwo( this.context.viewHeight );
		
		this._bloomFBO = this.context.resourceManager.createResource( "__bloom_fbo__", SmitearWebGL.Resources.FrameBufferObject  );
		this._bloomFBO.bind();
		this._bloomFBO.createRenderSurface( fboWidth * SmitearWebGL.Renderer.HDRRenderer.BloomImageScale,
											fboHeight * SmitearWebGL.Renderer.HDRRenderer.BloomImageScale );
		this._bloomFBO.createRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_BLOOM_TEX,
											informat, format, ubyte, sampler0 );
		this._bloomFBO.createRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_HBLUR_TEX,
											informat, format, ubyte, sampler2 );
		this._bloomFBO.createRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_VBLUR_TEX,
											informat, format, ubyte, sampler2 );
		this._bloomFBO.addRef();
		this._bloomFBO.unbind();
	}
	,
	_initAllCanvas : function()
	{
		//init luminance canvas
		this._luminanceCanvas = this.context.objectManager.createResource( "__luminance_canvas__", SmitearWebGL.Objects.Canvas.Hdr.Luminance );
		this._luminanceCanvas.addTexture( null );
		
		//init down sample canvas
		this._downsampleCanvas = this.context.objectManager.createResource( "__down sample_canvas__", SmitearWebGL.Objects.Canvas.Hdr.DownSample );
		this._downsampleCanvas.addTexture( this._luminanceFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTLumUsage.FBO_LUMINACE_TEX ) );
		for ( var i = 1; i < this._downSampleFBOs.length; ++ i )
			this._downsampleCanvas.addTexture( this._downSampleFBOs[i-1].getRenderTarget( 0 ) );
		this._downsampleCanvas.calculateOffsets();
		
		//init adapted lum canvas
		this._adaptationCanvas = this.context.objectManager.createResource( "__adaptation_canvas__", SmitearWebGL.Objects.Canvas.Hdr.Adaptation );
		this._adaptationCanvas.addTexture( this._downSampleFBOs[this._downSampleFBOs.length - 1].getRenderTarget( 0 ) );
		this._adaptationCanvas.addTexture( this._adaptationFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX1 ) );
		this._adaptationCanvas.addTexture( this._adaptationFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX2 ) );
		this._adaptationCanvas.calculateOffsets();
		
		//init bloom canvas
		this._bloomCanvas = this.context.objectManager.createResource( "__bloom_canvas__", SmitearWebGL.Objects.Canvas.Hdr.Bloom );
		this._bloomCanvas.addTexture( null );

		//init h_blur canvas
		this._hBlurCanvas = this.context.objectManager.createResource( "__h_blur_canvas__", SmitearWebGL.Objects.Canvas.Hdr.HBlur );
		this._hBlurCanvas.addTexture( this._bloomFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_BLOOM_TEX ) );
		this._hBlurCanvas.calculateOffsets();

		//init v_blur canvas
		this._vBlurCanvas = this.context.objectManager.createResource( "__v_blur_canvas__", SmitearWebGL.Objects.Canvas.Hdr.VBlur );
		this._vBlurCanvas.addTexture( this._bloomFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_HBLUR_TEX ) );
		this._vBlurCanvas.calculateOffsets();
		
		//init final canvas
		this._combineCanvas = this.context.objectManager.createResource( "__combine_canvas__", SmitearWebGL.Objects.Canvas.Hdr.Combine );
		this._combineCanvas.addTexture( null );
		this._combineCanvas.addTexture( this._adaptationFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX1 ) );
		this._combineCanvas.addTexture( this._adaptationFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX2 ) );
		this._combineCanvas.addTexture( this._bloomFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage.FBO_VBLUR_TEX ) );
		
		//debug only
		//init light-processing canvas
		if ( SmitearWebGL.Renderer.HDRRenderer._Debug )
		{
			this._ppCanvas = this.context.objectManager.createResource( "__post_processing_canvas__", SmitearWebGL.Objects.Canvas.LightPrePass.PostProcessing );
			this._ppCanvas.addTexture( this._adaptationFBO.getRenderTarget( SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage.FBO_Adaptation_TEX2 ) );
		}
	}
	,
	_createResource : function( params )
	{
		this._super();
		
		this._initFBO();
		this._initAllCanvas();
	}
	,
	_destroyResource : function()
	{
		this._super();
		
		this._luminanceCanvas.release();
		this._downsampleCanvas.release();
		this._adaptationCanvas.release();
		this._bloomCanvas.release();
		this._hBlurCanvas.release();
		this._vBlurCanvas.release();
		this._combineCanvas.release();
		
		this._luminanceCanvas = null;
		this._downsampleCanvas = null;
		this._adaptationCanvas = null;
		this._bloomCanvas = null;
		this._hBlurCanvas = null;
		this._vBlurCanvas = null;
		this._combineCanvas = null;
		
		this._luminanceFBO.release();
		this._adaptationFBO.release();
		this._bloomFBO.release();
		
		this._luminanceFBO = null;
		this._adaptationFBO = null;
		this._bloomFBO = null;
		
		for ( var i = 1; i < this._downSampleFBOs.length; ++ i )
		{
			this._downSampleFBOs[i].release();
		}
		this._downSampleFBOs = null;
		
		if ( SmitearWebGL.Renderer.HDRRenderer._Debug )
		{
			this._ppCanvas.release();
			this._ppCanvas = null;
		}
	}
}
);

SmitearWebGL.Renderer.HDRRenderer.LumImageSize = 256.0;
SmitearWebGL.Renderer.HDRRenderer.LumDownScale = 4;
SmitearWebGL.Renderer.HDRRenderer.BloomImageScale = 1.0 / 8.0;

SmitearWebGL.Renderer.HDRRenderer.RTLumUsage = 
{
	FBO_LUMINACE_TEX :	0,
	TOTAL :				1
}

SmitearWebGL.Renderer.HDRRenderer.RTBloomUsage = 
{
	FBO_BLOOM_TEX :			0,
	FBO_HBLUR_TEX :			1,
	FBO_VBLUR_TEX :			2,
	TOTAL :					3
}

SmitearWebGL.Renderer.HDRRenderer.RTAdaptationUsage = 
{
	FBO_Adaptation_TEX1 :	0,
	FBO_Adaptation_TEX2 :	1,
	TOTAL :					2
}

SmitearWebGL.Renderer.HDRRenderer.RTTClearColor = [0.0, 0.0, 0.0, 0.0];
SmitearWebGL.Renderer.HDRRenderer.RTFClearColor = [0.0, 0.0, 0.0, 1.0];
SmitearWebGL.Renderer.HDRRenderer.RTSClearColor = [1.0, 1.0, 1.0, 1.0];

SmitearWebGL.Renderer.HDRRenderer._Debug = false;